package com.lucky.common.util;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;

public class WSUtils {
	private static final Logger logger = LoggerFactory.getLogger(WSUtils.class);
	
	/**
	 * 调用远程web service 方法
	 * @param url 远程服务地址
	 * @param nameSpace 命名空间
	 * @param methodName web service 方法名
	 * @param paramName 参数名称
	 * @param data 报文处理
	 * @throws Exception
	 */
	private synchronized static String callService(String url,String nameSpace,String methodName,String paramName,String data) throws Exception{

		if(logger.isInfoEnabled()){
			logger.info("[WEBSERVICE]****************************************************************START");
			logger.info("[WEBSERVICE] >>>>>远程服务地址:"+url+"; 命名空间:"+nameSpace+"; 方法名:"+methodName+"; 参数名称:"+paramName);
			logger.info("[WEBSERVICE] >>>>>请求报文:"+data);
		}

		Map<String, String> params = new HashMap<String, String>();
		params.put("wsdl", url);
		params.put("method", methodName);
		Object[] object  = (Object[])WSUtils.callWebService1(params, new Object[]{data});
		String returnStr = object[0].toString();

		if(logger.isInfoEnabled()){
			logger.info("[WEBSERVICE] <<<<<响应报文:"+returnStr);
			logger.info("[WEBSERVICE]****************************************************************END");
		}

		return returnStr;
	}

	/**
	 * 调用远程web service 方法
	 * @param wsdl 远程服务地址
	 * @param methodName 方法名
	 * @param data 报文
	 * @throws Exception
	 */
	private synchronized static String callService4Http(String wsdl, String methodName, Object... data) throws Exception{

		if(logger.isInfoEnabled()){
			logger.info("[WEBSERVICE]****************************************************************START");
			logger.info("[WEBSERVICE] >>>>>远程服务地址:"+wsdl+"; 方法名:"+methodName);
			logger.info("[WEBSERVICE] >>>>>请求报文:"+data);
		}
		String returnStr = null;
		try {
			JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
			Client client = clientFactory.createClient(wsdl);

			//需要添加超时参数等,添加如下代码
			HTTPConduit conduit = (HTTPConduit) client.getConduit();// 获取静态映射
			HTTPClientPolicy policy = new HTTPClientPolicy();// 创建策略
			policy.setAllowChunking(false); //设置允许分块传输
			policy.setConnectionTimeout(180000);// 连接服务器超时时间3分钟
			policy.setReceiveTimeout(180000);//等待服务器响应超时时间3分钟
			conduit.setClient(policy); //将策略设置进端点

			Object[] result = client.invoke(methodName, data);
			returnStr = result[0].toString();

			if(logger.isInfoEnabled()){
				logger.info("[WEBSERVICE] <<<<<响应报文:"+returnStr);
				logger.info("[WEBSERVICE]****************************************************************END");
			}

		} catch (SocketTimeoutException e) {
			// TODO: handle exception
		} catch (Exception e) {
			// TODO: handle exception
		}

		return returnStr;
	}


	/**
	 * 调用远程https web service 方法
	 * @param wsdl 远程服务地址
	 * @param methodName web service 方法名
	 * @param data 报文处理
	 * @throws Exception
	 */
	private synchronized static String callService4Https(String wsdl, String methodName, Object... data) throws Exception{
		String returnStr = null;
		try {
			JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
			Client client = clientFactory.createClient(wsdl);

			//需要添加超时参数等,添加如下代码
			HTTPConduit conduit = (HTTPConduit) client.getConduit();// 获取静态映射
			HTTPClientPolicy policy = new HTTPClientPolicy();// 创建策略
			policy.setAllowChunking(false); //设置允许分块传输
			policy.setConnectionTimeout(180000);//超时3分钟
			policy.setReceiveTimeout(180000);//超时3分钟
			conduit.setClient(policy);
			conduit.setTlsClientParameters(TLSClientParameters("jksFilePath", "password"));

			Object[] result = client.invoke(methodName, data);
			returnStr = result[0].toString();

			if(logger.isInfoEnabled()){
				logger.info("[WEBSERVICE] <<<<<响应报文:"+returnStr);
				logger.info("[WEBSERVICE]****************************************************************END");
			}

		} catch (SocketTimeoutException e) {
			// TODO: handle exception
		} catch (Exception e) {
			// TODO: handle exception
		}

		return returnStr;
	}

	/**
	 * 调用远程web service 方法
	 * @param url 远程服务地址
	 * @param nameSpace 命名空间
	 * @param methodName web service 方法名
	 * @param paramName 参数名称
	 * @param data 报文处理
	 * @throws Exception
	 */
	private synchronized static String callService4Bank(String url,String nameSpace,String methodName,
														String paramName,String data,String file) throws Exception{

		String[] paramNameToArray = paramName.split(",");
		if(paramNameToArray.length < 2){
			throw new Exception("WebService的方法参数配置错误，应该包含data和file");
		}
		logger.info("数据库配置的参数名称1:"+paramNameToArray[0]+"********************************");
		logger.info("数据库配置的参数名称2:"+paramNameToArray[1]+"********************************");

		if(logger.isInfoEnabled()){
			logger.info("[WEBSERVICE]****************************************************************START");
			logger.info("[WEBSERVICE] >>>>>远程服务地址:"+url+"; 命名空间:"+nameSpace+"; 方法名:"+methodName+
					"; 参数1名称:"+paramNameToArray[0]+"; 参数2名称:"+paramNameToArray[1]);
			logger.info("[WEBSERVICE] >>>>>请求报文参数1:"+data +",请求报文参数2:"+file);
		}

//		Service serv = new Service();
//		Call call = (Call)serv.createCall();
//		// 3min 超时
//		call.setTimeout(new Integer(180000));
//		call.setTargetEndpointAddress(new URL(url));
//		call.setOperationName(new QName(nameSpace, methodName));
//	    call.addParameter(paramNameToArray[0],org.apache.axis.encoding.XMLType.XSD_STRING,javax.xml.rpc.ParameterMode.IN);// 操作的参数1
//	    call.addParameter(paramNameToArray[1],org.apache.axis.encoding.XMLType.XSD_STRING,javax.xml.rpc.ParameterMode.IN);// 操作的参数2
//		call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);
//		// call.setUseSOAPAction(true);
		logger.info("开始调用银行服务端接口");
//		String returnStr = (String)call.invoke(new Object[] {data,file});

		Map<String, String> params = new HashMap<String, String>();
		params.put("wsdl", url);
		params.put("method", methodName);
		Object[] object  = (Object[])WSUtils.callWebService1(params, new Object[]{data,file});
		String returnStr = object[0].toString();

		logger.info("结束调用银行服务端接口");
		if(logger.isInfoEnabled()){
			logger.info("[WEBSERVICE] <<<<<响应报文:"+returnStr);
			logger.info("[WEBSERVICE]****************************************************************END");
		}

		return returnStr;
	}

	private static TLSClientParameters TLSClientParameters(String jksFilePath, String password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException{
		TLSClientParameters tlsClientParameters = new TLSClientParameters();

		// 密钥类型
		KeyStore keyStore = KeyStore.getInstance("JKS"); //key.jks
//		KeyStore keyStore = KeyStore.getInstance("pkcs12");//key.p12
		// // 读取信任仓库  , 加载密钥
		keyStore.load(new FileInputStream(jksFilePath), password.toCharArray());

		//set trust keystore
		// 信任仓库的默认算法X509
		String trustAlg = TrustManagerFactory.getDefaultAlgorithm();
		// 获取信任仓库工厂
		TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(trustAlg);
		// 读取信任仓库
		trustFactory.init(keyStore);

		//set keystore
		// 获取默认的 X509算法
		String kayAlg = KeyManagerFactory.getDefaultAlgorithm();
		// 创建密钥管理工厂
		KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(kayAlg);
		keyFactory.init(keyStore, password.toCharArray());

//		tlsClientParameters.setSecureSocketProtocol("SSL");
		tlsClientParameters.setDisableCNCheck(true);
		tlsClientParameters.setTrustManagers(trustFactory.getTrustManagers());
		tlsClientParameters.setKeyManagers(keyFactory.getKeyManagers());

		return tlsClientParameters;
	}


	/**
	 *   	wsdl=
	 * 		method=
	 * 		data=
	 * 		file=
	 * @return return a string
	 * @throws Exception
	 */
	private static String callWebService(Map<String, String> params, Map<String, String> datas) throws Exception {
		String res = null;
		try {
//			CxfClient.setProxy(params);

			JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
			Client client = clientFactory.createClient(params.get("wsdl"));

			// 需要添加超时参数等,添加如下代码
			HTTPConduit conduit = (HTTPConduit) client.getConduit();// 获取静态映射
			HTTPClientPolicy policy = new HTTPClientPolicy();// 创建策略
//			policy.setAllowChunking(false); // 设置允许分块传输
			policy.setConnectionTimeout(60000);// 连接服务器超时时间3分钟
			policy.setReceiveTimeout(60000);// 等待服务器响应超时时间3分钟
			policy.setMaxRetransmits(60000);

			conduit.setClient(policy); // 将策略设置进端点
			conduit.setTlsClientParameters(getTLSClientParameters());  //设置SSL证书

			logger.info("开始调用银行服务端接口");
			Object[] result = client.invoke(params.get("method"), datas.get("data"), datas.get("file"));
			res = result[0].toString();
			logger.info("返回报文: "+ res);
			logger.info("结束调用银行服务端接口");

		} catch (SocketTimeoutException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}

	private static Object callWebService1(Map<String, String> params, Object[] datas) throws Exception {
		Object[] result  = null;
		try {
//			CxfClient.setProxy(params);
			JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
			Client client = clientFactory.createClient(params.get("wsdl"));

			// 需要添加超时参数等,添加如下代码
			HTTPConduit conduit = (HTTPConduit) client.getConduit();// 获取静态映射
			HTTPClientPolicy policy = new HTTPClientPolicy();// 创建策略
			policy.setConnectionTimeout(60000);// 连接服务器超时时间3分钟
			policy.setReceiveTimeout(60000);// 等待服务器响应超时时间3分钟
			policy.setMaxRetransmits(60000);

			conduit.setClient(policy); // 将策略设置进端点
//			conduit.setTlsClientParameters(getTLSClientParameters());  //设置SSL证书

			result = client.invoke(params.get("method"), datas);
			if(result.length==0){
				return null;
			}

			logger.info("返回报文: "+ result[0].toString());
			logger.info("结束调用银行服务端接口");

		} catch (SocketTimeoutException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return result;
	}

	/**
	 * @return
	 * @throws KeyStoreException
	 * @throws NoSuchAlgorithmException
	 * @throws CertificateException
	 * @throws IOException
	 * @throws UnrecoverableKeyException
	 */
	private static TLSClientParameters getTLSClientParameters() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException{

		TLSClientParameters tlsClientParameters = new TLSClientParameters();

		X509TrustManager trustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
										   String paramString) throws CertificateException {
			}

			@Override
			public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
										   String paramString) throws CertificateException {
			}

			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};

		TrustManager[] trustAllCerts = new TrustManager[] {trustManager};
		tlsClientParameters.setTrustManagers(trustAllCerts);

		// disables verification of the common name (the host for which the certificate has been issued)
		tlsClientParameters.setDisableCNCheck( true );

		return tlsClientParameters;
	}

}
